/**
 * Copyright (c) 2009  Collaborative Development Group, C.S. Dept., University of Bari
 *
 * All rights reserved. This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0  which accompanies this distribution, and is 
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package it.uniba.di.cdg.penelope;

import static it.uniba.di.cdg.penelope.PenelopeExtensionFactory.PROPERTY_BUNDLE_NAME;
import static org.eclipse.core.runtime.RegistryFactory.getRegistry;
import static org.ops4j.peaberry.Peaberry.osgiModule;
import static org.ops4j.peaberry.eclipse.EclipseRegistry.eclipseRegistry;
import it.uniba.di.cdg.penelope.internal.LazyInjector;

import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.Properties;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.eclipse.core.runtime.IConfigurationElement;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

import com.google.inject.Injector;
import com.google.inject.Module;

/**
 * Dependency Injection bridge between Guice and Eclipse. It captures the bundle lifecycles, builds the Injector and 
 * publishes it as OSGi service. This will allow the usage of the same injector through Eclipse' Extension Factories.
 * See {@link PenelopeExtensionFactory}.
 */
@Aspect
public abstract class AbstractDependencyInjectionAspect extends AbstractBundleLifecycleAwareAspect {

	public static final String XP_MODULES = "it.uniba.di.cdg.penelope.modules";

	private Injector injector;
	private ServiceRegistration injectorRegistration;
	
	/* (non-Javadoc)
	 * @see it.uniba.di.cdg.penelope.AbstractBundleLifecycleAwareAspect#onBundleActivated(org.osgi.framework.BundleContext)
	 */
	@Override
	protected void onBundleActivated( BundleContext bc ) {
		super.onBundleActivated( bc );
	
		injector = createInjector();
		
		Dictionary<Object, Object> properties = new Properties();
		properties.put( PROPERTY_BUNDLE_NAME, bc.getBundle().getSymbolicName() );
		
		injectorRegistration = bc.registerService( Injector.class.getName(), injector, properties );
	}
	
	/* (non-Javadoc)
	 * @see it.uniba.di.cdg.penelope.AbstractBundleLifecycleAwareAspect#onBundleDeactivating(org.osgi.framework.BundleContext)
	 */
	@Override
	protected void onBundleDeactivating( BundleContext bc ) {
		if (injectorRegistration != null) {
			injectorRegistration.unregister();
			injectorRegistration = null;
			injector = null;
		}
		super.onBundleDeactivating( bc );
	}
	
	@Pointcut( "" )
	protected abstract void injectionScope();
	
	@Pointcut( "initialization( (@it.uniba.di.cdg.penelope.annotation.Injectable *)+.new(..) ) && this( object )" )
	public void onObjectCreation( Object object ) {}
	
	@After( "onObjectCreation( object )" )
	public void afterObjectCreated( Object object ) {
		getInjector().injectMembers( object );
	}
	
	/**
	 * Create the Injector. By default:
	 * <ol>
	 * <li>will look for the modules declared in the <strong>it.uniba.di.cdg.penelope.modules</strong> extension point;</li>
	 * <li>create a new {@link LazyInjector} instance, in order to support lazy creation of the real injector.</li>
	 * </ol>
	 * <strong>Note:</strong>This method should not be reimplemented by clients but can be useful for testing purposes.
	 * @return the {@link Injector} instance
	 */
	protected Injector createInjector() {
		return new LazyInjector( getModulesFromExtensionPoint() );
	}

	/**
	 * Set the injector.
	 * <strong>Note:</strong>This method should not be reimplemented by clients but can be useful for testing purposes.
	 * @param injector
	 */
	protected void setInjector( Injector injector ) {
		this.injector = injector;
	}
	
	/**
	 * Returns the current injector instance.
	 * <strong>Note:</strong>This method should not be reimplemented by clients but can be useful for testing purposes.
	 * @return the injector
	 */
	protected Injector getInjector() {
		return injector;
	}

	/**
	 * Returns the modules list declared for the <strong>it.uniba.di.cdg.penelope.modules</strong> extensions for the
	 * current bundle.
	 * @return the modules declared.
	 */
	private List<Module> getModulesFromExtensionPoint() {
		List<Module> modules = null;
		try {
	        modules = new ArrayList<Module>();
	        modules.add( osgiModule( getBundleContext(), eclipseRegistry() ));

	        String wantedId = getBundleContext().getBundle().getSymbolicName();
	        for (final IConfigurationElement e : getRegistry().getConfigurationElementsFor( XP_MODULES )) {
	        	if (wantedId.equals( e.getContributor().getName() )) {
	        		modules.add((Module) e.createExecutableExtension( "class" ));
	        	}
	        }
		} catch (Throwable e) {
			throw new DependencyInjectionError( e );
		}
		return modules;
	}
}
